---------------------------------------------------------------------------------------------------- -- Physic door control ---------------------------------------------------------------------------------------------------- -- Исходный скрипт: Evgeniy Negrobov (Jon) jon@gsc-game.kiev.ua -- Перевод на xr_logic: Andrey Fidrya (Zmey) af@svitonline.com -- Доработка: Oleg Kreptul (Haron) haronk@ukr.net ---------------------------------------------------------------------------------------------------- class "action_door" function action_door:__init(obj, storage) --printf ("action_door::action_door() called") self.object = obj self.st = storage self.snd_obj = nil storage.door_action = self end function action_door:reset_scheme(loading) --printf("_bp: action_door:reset_scheme: self.object:name()='%s'", self.object:name()) self.st.signals = {} self.initialized = false local ph_shell = self.object:get_physics_shell() if not ph_shell then return end self.joint = ph_shell:get_joint_by_bone_name("door") self.low_limits, self.hi_limits = 0, 0 self.low_limits, self.hi_limits = self.joint:get_limits(self.low_limits, self.hi_limits, 0) self.block = false self.soundless_block = false self.show_tips = self.st.show_tips local disable_snd -- Звук нужно отключить, только если скрипт двери для этого объекта используется -- впервые (т.е. после загрузки уровня или после загрузки сохраненной игры), -- иначе игрок услышит хлопок... if not self.st.script_used_more_than_once then disable_snd = true self.st.script_used_more_than_once = true end if self.st.closed then --' Если дверь уже закрыта - то нужно закрыть без звука if self:is_closed() then disable_snd = true end self:close_door(disable_snd) else self:open_door(disable_snd) end self.object:set_nonscript_usable(false) self.initialized = true end function action_door:update(delta) --printf("_bp: %s action_door:update()",self.object:name(),delta) if not self.initialized then printf("object '%s': door failed to initialize", self.object:name()) end if xr_logic.try_switch_to_another_section(self.object, self.st, db.actor) then return end end function action_door:fastcall() if not self.initialized then return false end if self.block and self:is_closed() then self:close_action() self.object:on_door_is_closed() return true end return false end function action_door:open_fastcall() if not self.initialized then return false end if self:is_open() then local ph_obj = self.object:get_physics_object() ph_obj:unset_door_ignore_dynamics() --'Игнорирование динамики на момент открытия/закрытия self.object:on_door_is_open() return true end return false end function action_door:close_action() --printf("_bp: close_action(): %d", time_global()) -- Закрыли до предела, будем блокировать if self.st.no_force == true then self.joint:set_max_force_and_velocity(0, 0, 0) else self.joint:set_max_force_and_velocity(10000, 1, 0) --!!! Фиксируем кость. local ph_shell = self.object:get_physics_shell() if ph_shell then local ph_element = ph_shell:get_element_by_bone_name("door") if not ph_element:is_fixed() then --printf("FIX") ph_element:fix() end end end local ph_obj = self.object:get_physics_object() ph_obj:unset_door_ignore_dynamics() --'Игнорирование динамики на момент открытия/закрытия self.block = false -- Отыграть звук когда дверь захлопнулась: if not self.soundless_block and self.st.snd_close_stop then xr_sound.set_sound_play(self.object:id(), self.st.snd_close_stop) end end function action_door:open_door(disable_snd) --printf("_bp: [%s] action_door:open_door()", self.object:name()) if not disable_snd then if self.st.snd_open_start then xr_sound.set_sound_play(self.object:id(), self.st.snd_open_start) end end self.object:set_fastcall(self.open_fastcall,self) --!!! Тут надо снять фиксацию кости local ph_shell = self.object:get_physics_shell() if ph_shell then local ph_element = ph_shell:get_element_by_bone_name("door") if ph_element:is_fixed() then --printf("RELEASE") ph_element:release_fixed() local ph_obj = self.object:get_physics_object() ph_obj:set_door_ignore_dynamics() --'Игнорирование динамики на момент открытия/закрытия end end if self.st.no_force == true then self.joint:set_max_force_and_velocity(0, 0, 0) else self.joint:set_max_force_and_velocity(2100, -3, 0) end self.block = false if self.show_tips and self.st.tip_close then self.object:set_tip_text(self.st.tip_close) end end function action_door:is_closed() local angle if self.st.slider then angle = -self.joint:get_axis_angle(0) else angle = self.joint:get_axis_angle(90) end --printf("_bp[%s]: action_door:update(): angle %f limits %f, %f", -- self.object:name(), angle, self.low_limits, self.hi_limits) if angle <= self.low_limits + 0.02 then --printf("_bp: close_check(): true") return true end return false end function action_door:is_open() local angle if self.st.slider then angle = -self.joint:get_axis_angle(0) else angle = self.joint:get_axis_angle(90) end --printf("_bp[%s]: action_door:update(): angle %f limits %f, %f", -- self.object:name(), angle, self.low_limits, self.hi_limits) if angle >= self.hi_limits - 0.02 then --printf("_bp: open_check(): true") return true end return false end function action_door:close_door(disable_snd) --printf("_bp: [%s] action_door:close_door()", self.object:name()) -- local disable_snd = self:is_closed() if not disable_snd then if self.st.snd_close_start then xr_sound.set_sound_play(self.object:id(), self.st.snd_close_start) end end -- Ставим коллбек для отлова окончания закрытия двери (чтобы заблокировать ее и проиграть звук, если нужно): self.object:set_fastcall(self.fastcall,self) --printf("_bp: action_door:close_door()") if self.st.no_force == true then self.joint:set_max_force_and_velocity(0, 0, 0) else self.joint:set_max_force_and_velocity(200, 3, 0) end self.block = true -- Закрываем и блокируем self.soundless_block = disable_snd local ph_obj = self.object:get_physics_object() ph_obj:set_door_ignore_dynamics() --'Игнорирование динамики на момент открытия/закрытия if self.show_tips then if self.st.locked == true and self.st.tip_unlock then self.object:set_tip_text(self.st.tip_unlock) return end if self.st.tip_open then self.object:set_tip_text(self.st.tip_open) end end end function action_door:try_switch() --printf("_bp: action_door: object '%s': try_switch on_use=%s", self.object:name(),self.st.on_use) if self.st.on_use then if xr_logic.switch_to_section(self.object, self.st.ini,xr_logic.pick_section_from_condlist(db.actor, self.object, self.st.on_use.condlist)) then return true end end return false end function action_door:use_callback(door, actor) --printf("door=%s actor=%s",door and door:name(),actor and actor:name()) if self.st.locked then if self.st.snd_open_start then xr_sound.set_sound_play(self.object:id(), self.st.snd_open_start) end end self:try_switch() end function action_door:hit_callback(obj, amount, local_direction, who, bone_index) --printf("DOOR: hit_callback: obj='%s', amount=%d, who='%s', bone='%s'", obj and obj:name(), amount, who and who:name(), bone_index) if self.st.hit_on_bone[bone_index] ~= nil then local section = xr_logic.pick_section_from_condlist(db.actor, self.object, self.st.hit_on_bone[bone_index].state) xr_logic.switch_to_section(obj, self.st.ini, section) return end end function action_door:deactivate() self.object:set_tip_text("") end --------------------------------------------------------------------------------------------------------------------- function add_to_binder(npc, ini, scheme, section, storage) --printf("DEBUG: add_to_binder: scheme='%s', section='%s'", scheme, section) npc:register_door_for_npc() db.level_doors[npc:id()] = npc:position() local new_action = action_door(npc, storage) -- Зарегистрировать все actions, в которых должен быть вызван метод reset_scheme при изменении настроек схемы: xr_logic.subscribe_action_for_events(npc, storage, new_action) end function set_scheme(npc, ini, scheme, section, gulag_name) local st = xr_logic.assign_storage_and_bind(npc, ini, scheme, section) st.logic = xr_logic.cfg_get_switch_conditions(ini, section, npc) local not_for_npcs = { ["dynamics\\door\\bookcase_village_50x90_ph_door_l"] = true, ["dynamics\\door\\bookcase_village_50x90_ph_door_r"] = true, ["dynamics\\door\\box_wooden_01_door"] = true, ["dynamics\\door\\cabinet_child_45x80_01_door"] = true, ["dynamics\\door\\cabinet_child_350x200_01_door"] = true, ["dynamics\\door\\cabinet_village_140x210_ph_door_l"] = true, ["dynamics\\door\\cabinet_village_140x210_ph_door_r"] = true, ["dynamics\\door\\codedoor_key"] = true, ["dynamics\\door\\codedoor_left"] = true, ["dynamics\\door\\codedoor_right"] = true, ["dynamics\\door\\door_locker"] = true, ["dynamics\\door\\safe_01_ph_door"] = true, ["dynamics\\door\\shelf_metal_160x200_door_l"] = true, ["dynamics\\door\\shelf_metal_160x200_door_r"] = true, ["dynamics\\door\\shkaf_lab_01_door_l"] = true, ["dynamics\\door\\shkaf_lab_01_door_r"] = true, ["dynamics\\door\\shkaf_work_01_door"] = true, ["dynamics\\door\\shop_fridge_1_door_l"] = true, ["dynamics\\door\\shop_fridge_1_door_r"] = true, ["dynamics\\door\\shop_fridge_2_door"] = true } st.closed = ini:r_bool_ex(section,"closed",true) st.hit_on_bone = utils_data.parse_data_1v(npc, ini:r_string_ex(section,"hit_on_bone") or "") st.locked = ini:r_bool_ex(section,"locked",false) st.no_force = ini:r_bool_ex(section,"no_force",false) st.not_for_npc = ini:r_bool_ex(section,"not_for_npc",not_for_npcs[npc:get_visual_name()] or false) st.on_use = xr_logic.cfg_get_condlist(ini, section, "on_use", npc) st.show_tips = ini:r_bool_ex(section,"show_tips",true) st.slider = ini:r_bool_ex(section,"slider",false) st.snd_close_start = ini:r_string_ex(section,"snd_close_start") or "trader_door_close_start" st.snd_close_stop = ini:r_string_ex(section,"snd_close_stop") or "trader_door_close_stop" st.snd_open_start = ini:r_string_ex(section,"snd_open_start") or "trader_door_open_start" st.tip_close = ini:r_string_ex(section,"tip_close") or "tip_door_close" st.tip_open = ini:r_string_ex(section,"tip_open") or "tip_door_open" st.tip_unlock = ini:r_string_ex(section,"tip_open") or "tip_door_locked" st.unlock_for_npc = ini:r_bool_ex(section,"unlock_for_npc",false) if (st.unlock_for_npc and npc:is_door_locked_for_npc()) then npc:unlock_door_for_npc() elseif (st.locked == true or st.not_for_npc == true) then if not npc:is_door_locked_for_npc() then npc:lock_door_for_npc() end elseif (npc:is_door_locked_for_npc()) then npc:unlock_door_for_npc() end end function try_to_open_door(npc,opened_doors) local npc_position = npc:position() for id,position in pairs(db.level_doors) do if (db.storage[id].ph_door and npc_position:distance_to_sqr(position) <= 3) then local action = db.storage[id].ph_door.door_action if action.st.not_for_npc == false and action.st.closed == true then action:use_callback() opened_doors[id] = true end end end end function try_to_close_door(npc, doors) local npc_position = npc:position() local closed_doors = {} for id, v in pairs(doors) do if (db.storage[id].ph_door) then if (npc_position:distance_to_sqr(db.level_doors[id]) > 3) then local action = db.storage[id].ph_door.door_action if action.st.not_for_npc == false and action.st.closed ~= true then action:use_callback() closed_doors[id] = true else closed_doors[id] = true end end else closed_doors[id] = true end end for id,v in pairs(closed_doors) do doors[id] = nil end end